﻿using DevExpress.ExpressApp.DC;
using EasyXaf.LowCode.WorkflowEditors.Models.Activities;
using System.ComponentModel;
using System.Xml.Linq;

namespace EasyXaf.LowCode.WorkflowEditors.Models;

[DomainComponent]
public class Workflow : FlowObject, IWorkflow
{
    [Browsable(false)]
    public string SelectedActivityId
    {
        get => GetPropertyValue<string>();
        set
        {
            var oldValue = GetPropertyValue<string>();
            if (SetPropertyValue(value))
            {
                FindActivity(oldValue)?.RaisePropertyChanged(nameof(IActivity.IsSelected));
                FindActivity(value)?.RaisePropertyChanged(nameof(IActivity.IsSelected));
            }
        }
    }

    [Browsable(false)]
    public IContainer CurrentContainer
    {
        get => GetPropertyValue<IContainer>() ?? this;
        set
        {
            if (SetPropertyValue(value))
            {
                OnCurrentContainerChanged();
            }
        }
    }

    [Browsable(false)]
    public ObjectCollection<IActivity> Activities { get; }

    IContainer ISequence.Container => null;

    IWorkflow ISequence.Workflow => this;

    ObjectCollection<IActivity> ISequence.Activities => Activities;

    string IWorkflow.CloningActivity { get; set; }

    string IWorkflow.CuttingActivity { get; set; }

    string IWorkflow.CloningBranch { get; set; }

    string IWorkflow.CuttingBranch { get; set; }

    public event EventHandler CurrentContainerChanged;

    public event EventHandler WorkflowChanged;

    public Workflow()
    {
        Activities = new ObjectCollection<IActivity>(this);
    }

    private void OnCurrentContainerChanged()
    {
        CurrentContainerChanged?.Invoke(this, EventArgs.Empty);
    }

    private void OnWorkflowChanged()
    {
        WorkflowChanged?.Invoke(this, EventArgs.Empty);
    }

    public override void OnCloned()
    {
        base.OnCloned();

        foreach (var activity in Activities)
        {
            activity.OnCloned();
        }
    }

    public void ClearClipboard()
    {
        ((IWorkflow)this).CloningActivity = null;
        ((IWorkflow)this).CuttingActivity = null;
        ((IWorkflow)this).CloningBranch = null;
        ((IWorkflow)this).CuttingBranch = null;
    }

    public ISequence FindSequence(string sequenceId)
    {
        return FindSequence(sequenceId, this);
    }

    public ISequence FindSequence(string sequenceId, ISequence owner)
    {
        var sequences = new List<ISequence>();
        sequences.AddRange(owner.Activities.OfType<IBranchesActivity>().SelectMany(b => b.Branches));
        sequences.AddRange(owner.Activities.OfType<IContainerActivity>());
        var sequence = sequences.FirstOrDefault(f => f.Id == sequenceId);
        if (sequence == null)
        {
            foreach (var s in sequences)
            {
                sequence = s.FindSequence(sequenceId);
                if (sequence != null)
                {
                    break;
                }
            }
        }
        return sequence;
    }

    public IActivity FindActivity(string activityId)
    {
        return FindActivity(activityId, this);
    }

    public IActivity FindActivity(string activityId, ISequence owner)
    {
        var activity = owner.Activities.FirstOrDefault(a => a.Id == activityId);
        if (activity == null)
        {
            var sequences = new List<ISequence>();
            sequences.AddRange(owner.Activities.OfType<IBranchesActivity>().SelectMany(b => b.Branches));
            sequences.AddRange(owner.Activities.OfType<IContainerActivity>());
            foreach (var s in sequences)
            {
                activity = s.FindActivity(activityId);
                if (activity != null)
                {
                    break;
                }
            }
        }
        return activity;
    }

    public INode FindNode(string nodeId)
    {
        return FindNode(nodeId, this);
    }

    public INode FindNode(string nodeId, ISequence owner)
    {
        var sequence = FindSequence(nodeId, owner);
        if (sequence is INode node)
        {
            return node;
        }

        var activity = FindActivity(nodeId, owner);
        if (activity != null)
        {
            return activity;
        }

        return null;
    }

    public IActivity CreateActivityFromXml(XElement element, ISequence sequence)
    {
        var activityTypeInfo = Activity.ActivityTypeInfos.FirstOrDefault(a => a.Name == element.Name)
            ?? throw new Exception($"没有找到名称为'{element.Name}'的活动类型");
        var constructorInfo = activityTypeInfo.Type.GetConstructor(new Type[] { typeof(IWorkflow), typeof(ISequence) });
        var activity = (IActivity)constructorInfo.Invoke(new object[] { this, sequence });
        activity.ReadFromXml(element);
        return activity;
    }

    public IBranch CreateBranchFromXml(XElement element, IActivity activity)
    {
        var branchTypeInfo = Branch.BranchTypeInfos.FirstOrDefault(a => a.Name == element.Name)
            ?? throw new Exception($"没有找到名称为'{element.Name}'的分支类型");
        var constructorInfo = branchTypeInfo.Type.GetConstructor(new Type[] { typeof(IWorkflow), typeof(IActivity) });
        var branch = (IBranch)constructorInfo.Invoke(new object[] { this, activity });
        branch.ReadFromXml(element);
        return branch;
    }

    public void ChangeCurrentContainer(string containerId)
    {
        if (containerId == Id)
        {
            CurrentContainer = this;
        }
        else
        {
            if (FindSequence(containerId) is IContainer container)
            {
                CurrentContainer = container;
            }
            else
            {
                throw new Exception($"Id为'{containerId}'的容器不存在");
            }
        }
    }

    public void RaiseWorkflowChanged()
    {
        OnWorkflowChanged();
    }

    public override void OnCreated()
    {
        base.OnCreated();

        Activities.Add(new StartActivity(this, this).Created());
        Activities.Add(new EndActivity(this, this).Created());
    }

    public override void WriteToXml(XElement element)
    {
        base.WriteToXml(element);

        if (Activities.Any())
        {
            var activitiesElement = new XElement("Activities");
            element.Add(activitiesElement);
            foreach (var activity in Activities)
            {
                var activityElement = new XElement(activity.GetType().Name);
                activity.WriteToXml(activityElement);
                activitiesElement.Add(activityElement);
            }
        }
    }

    public override void ReadFromXml(XElement element)
    {
        base.ReadFromXml(element);

        if (element.Element("Activities") is XElement activitiesElement)
        {
            foreach (var activityElement in activitiesElement.Elements())
            {
                Activities.Add(CreateActivityFromXml(activityElement, this));
            }
        }
    }
}